package com.jing.yao.component.controller;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jing.yao.annotation.ExcelFileName;
import com.jing.yao.annotation.Permission;
import com.jing.yao.bean.ResponseBean;
import com.jing.yao.component.dto.YaoBaseDTO;
import com.jing.yao.component.entity.YaoBaseEntity;
import com.jing.yao.component.params.page.PageParam;
import com.jing.yao.component.service.YaoBaseService;
import com.jing.yao.constant.ErrorCode;
import com.jing.yao.excel.ExcelUtil;
import com.jing.yao.exception.BusinessExceptionBuilder;
import com.jing.yao.holder.UserContentHolder;
import com.jing.yao.utils.YaoBeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static com.jing.yao.bean.ResponseBean.builder;
import static com.jing.yao.component.constants.ResponseCode.ENTITY_NOT_FOUND;
import static com.jing.yao.component.constants.ResponseCode.FAIL_CODE;


/**
 * 基础controller
 *
 * @author yao
 * @since 2020-06-13
 */
public abstract class YaoBaseController<P extends PageParam, T extends YaoBaseEntity, D extends YaoBaseDTO> {

    /**
     * 记录日志
     */
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 获取实际服务类
     *
     * @return
     */
    public abstract YaoBaseService<P, T> getService();

    /**
     * 获取当前Controller数据库实体Entity
     *
     * @return
     */
    public abstract T getEntity();

    /**
     * 获取当前Controller数据传输DTO
     *
     * @return
     */
    public abstract D getDTO();

    /**
     * 分页模板
     *
     * @param param
     * @return
     */
    @GetMapping("/pageList")
    public ResponseBean pageList(P param) {
        IPage iPage = getService().page(param);
        List<T> records = iPage.getRecords();
        List<D> list = records.stream().map(entity -> {
            D dto = getDTO();
            YaoBeanUtils.copyAndFormatter(entity, dto);
            return dto;
        }).collect(Collectors.toList());

        Page<D> pageDto = new Page<>();
        pageDto.setCurrent(iPage.getCurrent());
        pageDto.setRecords(list);
        pageDto.setPages(iPage.getPages());
        pageDto.setTotal(iPage.getTotal());
        pageDto.setSize(iPage.getSize());
        return successBean(pageDto);
    }


    /**
     * 根据ID查询相关记录
     *
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public ResponseBean selectById(@PathVariable("id") Long id) {
        logger.info("{}根据ID查询服务开始，id为：{}", this.getClass().getSimpleName(), id);
        T result = getService().selectOneById(id);
        if (result == null) {
            throw BusinessExceptionBuilder.build(ErrorCode.DETAIL_ID_NOT_EXIST);
        }
        ResponseBean responseBean = successBean(result);
        logger.info("{}根据ID查询结束，结果：{}", this.getClass().getSimpleName(), toJSONString(responseBean));
        return responseBean;
    }

    /**
     * 转换为JSON字符串
     *
     * @param object
     * @return
     */
    private String toJSONString(Object object) {
        ObjectMapper mapper = new ObjectMapper();
        String result = "";
        try {
            result = mapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
        }
        return result;
    }

    /**
     * 插入
     *
     * @param dto
     * @return
     * @throws Exception
     */
    @PostMapping
    @Permission(value = "add")
    public ResponseBean insert(@Valid @RequestBody D dto) {
        logger.info("{}新增服务开始，参数：{}", this.getClass().getSimpleName(), toJSONString(dto));

        ResponseBean responseBean = successBean();
        boolean insert;
        T entity = getEntity();
        try {
            //dto转为数据库实体
            BeanUtils.copyProperties(dto, entity);
            //新增前校验
            insert = getService().insert(entity);
        } catch (DuplicateKeyException exception) {
            exception.printStackTrace();
            return builder().code(FAIL_CODE).build();
        }

        //当插入记录小于1时，表示插入失败
        if (!insert) {
            responseBean = builder().code(FAIL_CODE).content(insert).build();
        }
        logger.info("{}新增服务结束，结果：{}", this.getClass().getSimpleName(), toJSONString(responseBean));
        return responseBean;
    }


    /**
     * 根据ID修改对饮记录
     *
     * @param dto
     * @return
     * @throws Exception
     */
    @PutMapping
    @Permission(value = "edit")
    public ResponseBean updateById(@Valid @RequestBody D dto) {
        String username = UserContentHolder.getContext().getUsername();
        logger.info("{}更新服务开始,更新人：{}，参数：{}", this.getClass().getSimpleName(), username, toJSONString(dto));
        T entity = getEntity();
        //dto转换entity
        BeanUtils.copyProperties(dto, entity);
        ResponseBean responseBean = null;
        boolean count;

        try {
            count = getService().update(entity);
        } catch (DuplicateKeyException exception) {
            return builder().code(FAIL_CODE).build();
        }

        //当更新记录小于1时，表示更新失败
        if (!count) {
            responseBean = builder().code(FAIL_CODE).content(count).build();
        }
        logger.info("{}更新服务结束，结果：{}", this.getClass().getSimpleName(), toJSONString(responseBean));

        return successBean(count);
    }


    /**
     * 根据ID删除指定记录,这里被删除的记录会进入删除记录表
     *
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    @Permission(value = "del")
    public ResponseBean deleteById(@PathVariable("id") Long id) {
        logger.info("{}删除服务开始，参数ID：{}", this.getClass().getSimpleName(), id);
        boolean deleteCount = getService().removeById(id);
        ResponseBean responseBean = successBean(deleteCount);
        //当删除记录小于1时，表示更新失败
        if (!deleteCount) {
            responseBean = builder().code(FAIL_CODE).build();
        }

        logger.info("{}删除服务结束，结果：{}", this.getClass().getSimpleName(), toJSONString(responseBean));
        return responseBean;
    }

    /**
     * 删除批量ID对应的记录
     *
     * @param ids
     * @return
     */
    @PostMapping("/delete/batch")
    @Permission(value = "del")
    public ResponseBean deleteBatchIds(@RequestBody List<Long> ids) {
        logger.info("{}批量删除服务开始，批量参数Ids：{}", this.getClass().getSimpleName(), toJSONString(ids));
        boolean deleteCount = getService().removeByIds(ids);

        ResponseBean responseBean = successBean(deleteCount);

        //当删除记录小于1时，表示更新失败
        if (!deleteCount) {
            responseBean = builder().code(ENTITY_NOT_FOUND).build();
        }
        logger.info("{}批量删除服务结束，结果：{}", this.getClass().getSimpleName(), toJSONString(responseBean));
        return responseBean;
    }

    @PostMapping("exportList")
    public void exportList(@Valid @RequestBody P param, HttpServletResponse response) {
        List<T> ts = getService().exportList(param, response);
        List<D> excelList = new ArrayList<>();
        ts.stream().forEach(entity -> {
            D dto = getDTO();
            YaoBeanUtils.copyAndFormatter(entity, dto);
            excelList.add(dto);
        });
        String fileName = "";
        if (getDTO().getClass().isAnnotationPresent(ExcelFileName.class)) {
            ExcelFileName annotation = getDTO().getClass().getAnnotation(ExcelFileName.class);
            fileName = annotation.value() + "导出";
        } else {
            fileName = "导出列表";
        }
        ExcelUtil.writeWithTemplate(fileName, getDTO().getClass(), excelList, response);
    }

    /**
     * 更新状态
     *
     * @param id
     * @param enabled
     * @return
     */
    @GetMapping("enabled/{id}/{enabled}")
    public ResponseBean enabled(@PathVariable("id") Long id, @PathVariable("enabled") Integer enabled) {
        getService().updateColumn(id, "enabled", enabled);
        return successBean();
    }

    /**
     * 构建成功响应实例
     *
     * @return
     */
    public ResponseBean successBean() {
        return builder().build();
    }

    /**
     * 构建成功响应实例
     *
     * @param data
     * @return
     */
    public ResponseBean successBean(Object data) {
        return builder().content(data).build();
    }
}
